package com.gemantic.glue.exec.context;

import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gemantic.glue.enums.ProcessHandler;
import com.gemantic.glue.exec.ExecutorHandler;
import com.gemantic.glue.handler.IGlueHandler;
import com.gemantic.glue.handler.MethodGlueHandler;
import com.gemantic.glue.log.JobFileAppender;
import com.gemantic.glue.thread.JobLogFileCleanThread;


/**
 * Created by xuxueli on 2016/3/2 21:14.
 */
public class GlueExecutor {
    private static final Logger logger = LoggerFactory.getLogger(GlueExecutor.class);

    // ---------------------- param ----------------------

    private String appname;
    private String logPath;
    private int logRetentionDays;


    public void setLogPath(String logPath) {
        this.logPath = logPath;
    }
    public void setLogRetentionDays(int logRetentionDays) {
        this.logRetentionDays = logRetentionDays;
    }

    private static ConcurrentMap<Long, ExecutorHandler> jobThreadRepository = new ConcurrentHashMap<Long, ExecutorHandler>();

    public static ExecutorHandler registerJobThread(long jobId, IGlueHandler handler, String removeOldReason){
        ExecutorHandler newExecutorHandler = new ExecutorHandler(jobId, handler);
        logger.info(">>>>>>>>>>> glue-handler regist JobThread success, jobId:{}, handler:{}", new Object[]{jobId, handler});

        ExecutorHandler oldExecutorHandler = jobThreadRepository.put(jobId, newExecutorHandler);	// putIfAbsent | oh my god, map's put method return the old value!!!
        if (oldExecutorHandler != null) {
            oldExecutorHandler.toStop(removeOldReason);
        }

        return newExecutorHandler;
    }

    public static ExecutorHandler removeJobThread(int jobId, String removeOldReason){
        ExecutorHandler oldExecutorHandler = jobThreadRepository.remove(jobId);
        if (oldExecutorHandler != null) {
            oldExecutorHandler.toStop(removeOldReason);
            return oldExecutorHandler;
        }
        return null;
    }
    public static ExecutorHandler loadJobThread(long jobId){
        return jobThreadRepository.get(jobId);
    }

    public void start() throws Exception {
        // init logpath
        JobFileAppender.initLogPath(logPath);
        // init JobLogFileCleanThread
        JobLogFileCleanThread.getInstance().start(logRetentionDays);
    }

    public void destroy(){
        jobHandlerRepository.clear();
        jobThreadRepository.clear();
    }




    // ---------------------- job handler repository ----------------------
    private static ConcurrentMap<String, IGlueHandler> jobHandlerRepository = new ConcurrentHashMap<String, IGlueHandler>();
    public static IGlueHandler loadJobHandler(String name){
        return jobHandlerRepository.get(name);
    }
    public static IGlueHandler registJobHandler(String name, IGlueHandler jobHandler){
        logger.info(">>>>>>>>>>> glue-handler register jobhandler success, name:{}, jobHandler:{}", name, jobHandler);
        return jobHandlerRepository.put(name, jobHandler);
    }
    protected void registJobHandler(ProcessHandler processHandler, Object bean, Method executeMethod){
        if (processHandler == null) {
            return;
        }

        String name = processHandler.value();
        //make and simplify the variables since they'll be called several times later
        Class<?> clazz = bean.getClass();
        String methodName = executeMethod.getName();
        if (name.trim().length() == 0) {
            throw new RuntimeException("glue-handler method-jobhandler name invalid, for[" + clazz + "#" + methodName + "] .");
        }
        if (loadJobHandler(name) != null) {
            throw new RuntimeException("glue-handler jobhandler[" + name + "] naming conflicts.");
        }

        executeMethod.setAccessible(true);

        // init and destroy
        Method initMethod = null;
        Method destroyMethod = null;

        if (processHandler.init().trim().length() > 0) {
            try {
                initMethod = clazz.getDeclaredMethod(processHandler.init());
                initMethod.setAccessible(true);
            } catch (NoSuchMethodException e) {
                throw new RuntimeException("glue-handler method-jobhandler initMethod invalid, for[" + clazz + "#" + methodName + "] .");
            }
        }
        if (processHandler.destroy().trim().length() > 0) {
            try {
                destroyMethod = clazz.getDeclaredMethod(processHandler.destroy());
                destroyMethod.setAccessible(true);
            } catch (NoSuchMethodException e) {
                throw new RuntimeException("glue-handler method-jobhandler destroyMethod invalid, for[" + clazz + "#" + methodName + "] .");
            }
        }

        // registry jobhandler
        registJobHandler(name, new MethodGlueHandler(bean, executeMethod, initMethod, destroyMethod));
    }
}
